gl: Make all user GdkGLContexts not attached to any window
authorAlexander Larsson <alexl@redhat.com>
Thu, 9 Oct 2014 14:09:05 +0000 (16:09 +0200)
committerMatthias Clasen <mclasen@redhat.com>
Mon, 13 Oct 2014 14:43:31 +0000 (10:43 -0400)
We make user facing gl contexts not attached to a surface if possible,
or attached to dummy surfaces. This means nothing can accidentally
read/write to the toplevel back buffer.

gdk/gdkglcontext.c
gdk/gdkwindow.c
gdk/gdkwindowimpl.h
gdk/wayland/gdkdisplay-wayland.h
gdk/wayland/gdkglcontext-wayland.c
gdk/wayland/gdkglcontext-wayland.h
gdk/wayland/gdkprivate-wayland.h
gdk/wayland/gdkwindow-wayland.c
gdk/x11/gdkglcontext-x11.c
gdk/x11/gdkglcontext-x11.h

index 58eaa0abb4dcdb860a0172e5f406154764817e29..9a5f457ef5cdf31988602993aa721da7d3a6cd70 100644 (file)
  * OpenGL drawing context.
  *
  * #GdkGLContexts are created for a #GdkWindow using gdk_window_create_gl_context(), and
- * the context will be tied to the native window backing that window, matching the
- * GdkVisual of the window.
- *
- * A #GdkGLContexts normal framebuffer draws directly on to the back buffer of the native
- * window backing the #GdkWindow, so its not allowed to draw directly to that, as the
- * gdk repaint system is in full control of that. Instead you can create render buffers
- * or textures and use gdk_cairo_draw_from_gl() in the draw function of your widget
- * to draw them. Then Gdk will handle the integration of your rendering with that of
- * other widgets.
+ * the context will match the GdkVisual of the window.
+ *
+ * A #GdkGLContexts is not tied to any particulare normal
+ * framebuffer. For instance, it cannot draw to the #GdkWindow back
+ * buffer. The gdk repaint system is in full control of the painting
+ * to that. Instead you can create render buffers or textures and use
+ * gdk_cairo_draw_from_gl() in the draw function of your widget to
+ * draw them. Then Gdk will handle the integration of your rendering
+ * with that of other widgets.
  *
  * Support for #GdkGLContext is platform specific, context creation can fail, returning
  * a %NULL context.
index a73725487bd65fea951faf0d33fe7e49cc0ac90d..21eddf013e1abd759e50d867b7075fe85314dc70 100644 (file)
@@ -2725,6 +2725,7 @@ gdk_window_get_paint_gl_context (GdkWindow *window, GError **error)
   if (window->impl_window->gl_paint_context == NULL)
     window->impl_window->gl_paint_context =
       GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window,
+                                                                  TRUE,
                                                                    GDK_GL_PROFILE_DEFAULT,
                                                                    NULL,
                                                                    error);
@@ -2738,8 +2739,9 @@ gdk_window_get_paint_gl_context (GdkWindow *window, GError **error)
  * @profile: the GL profile the context should target
  * @error: return location for an error
  *
- * Creates a new #GdkGLContext for the given window, matching the
- * framebuffer format to the visual of the #GdkWindow.
+ * Creates a new #GdkGLContext matching the
+ * framebuffer format to the visual of the #GdkWindow. The context
+ * is disconnected from any particular window or surface.
  *
  * If the creation of the #GdkGLContext failed, @error will be set.
  *
@@ -2763,6 +2765,7 @@ gdk_window_create_gl_context (GdkWindow    *window,
     return NULL;
 
   return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window,
+                                                                     FALSE,
                                                                       profile,
                                                                       paint_context,
                                                                       error);
index f6e9a638b7fecaa1cbefaad27fe6c203823b4afb..2c1bc1e07a1ffc73d851d4c80ddb658f3cb2625d 100644 (file)
@@ -290,6 +290,7 @@ struct _GdkWindowImplClass
   gboolean     (* show_window_menu)       (GdkWindow      *window,
                                            GdkEvent       *event);
   GdkGLContext *(*create_gl_context)      (GdkWindow      *window,
+                                          gboolean        attached,
                                            GdkGLProfile    profile,
                                            GdkGLContext   *share,
                                            GError        **error);
index daa8baf08d2365d7c3bff9150320ae23f5273c8f..3bad519a12aad718b33f61cf0a7dea2659a7b664 100644 (file)
@@ -89,6 +89,7 @@ struct _GdkWaylandDisplay
   guint have_egl_khr_create_context : 1;
   guint have_egl_buffer_age : 1;
   guint have_egl_swap_buffers_with_damage : 1;
+  guint have_egl_surfaceless_context : 1;
 };
 
 struct _GdkWaylandDisplayClass
index 5ddcdcd5467102bc2f7a5fe4af4e63b163492aae..b6f1e9974d7a5f5d50f8b05bbaadbcbe40599095 100644 (file)
@@ -76,7 +76,8 @@ gdk_wayland_window_invalidate_for_new_frame (GdkWindow      *window,
   egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window,
                                                     context_wayland->egl_config);
 
-  if (display_wayland->have_egl_buffer_age)
+  if (display_wayland->have_egl_buffer_age &&
+      gdk_gl_context_make_current (window->gl_paint_context))
     eglQuerySurface (display_wayland->egl_display, egl_surface,
                      EGL_BUFFER_AGE_EXT, &buffer_age);
 
@@ -203,6 +204,9 @@ gdk_wayland_display_init_gl (GdkDisplay *display)
   display_wayland->have_egl_swap_buffers_with_damage =
     epoxy_has_egl_extension (dpy, "EGL_EXT_swap_buffers_with_damage");
 
+  display_wayland->have_egl_surfaceless_context =
+    epoxy_has_egl_extension (dpy, "EGL_KHR_surfaceless_context");
+
   GDK_NOTE (OPENGL,
             g_print ("EGL API version %d.%d found\n"
                      " - Vendor: %s\n"
@@ -297,6 +301,7 @@ find_eglconfig_for_window (GdkWindow        *window,
 
 GdkGLContext *
 gdk_wayland_window_create_gl_context (GdkWindow     *window,
+                                     gboolean       attached,
                                       GdkGLProfile   profile,
                                       GdkGLContext  *share,
                                       GError       **error)
@@ -359,6 +364,7 @@ gdk_wayland_window_create_gl_context (GdkWindow     *window,
 
   context->egl_config = config;
   context->egl_context = ctx;
+  context->is_attached = attached;
 
   return GDK_GL_CONTEXT (context);
 }
@@ -404,7 +410,16 @@ gdk_wayland_display_make_gl_context_current (GdkDisplay   *display,
   context_wayland = GDK_WAYLAND_GL_CONTEXT (context);
   window = gdk_gl_context_get_window (context);
 
-  egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window, context_wayland->egl_config);
+  if (context_wayland->is_attached)
+    egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window, context_wayland->egl_config);
+  else
+    {
+      if (display_wayland->have_egl_surfaceless_context)
+       egl_surface = EGL_NO_SURFACE;
+      else
+       egl_surface = gdk_wayland_window_get_dummy_egl_surface (window->impl_window,
+                                                               context_wayland->egl_config);
+    }
 
   if (!eglMakeCurrent(display_wayland->egl_display, egl_surface,
                       egl_surface, context_wayland->egl_context))
index 475a28bef67eaae85f17cd4c1551c197516aa90f..3d4fe37017b0f8dd28c696625457cab11beb2092 100644 (file)
@@ -39,6 +39,7 @@ struct _GdkWaylandGLContext
 
   EGLContext egl_context;
   EGLConfig egl_config;
+  gboolean is_attached;
 };
 
 struct _GdkWaylandGLContextClass
@@ -48,6 +49,7 @@ struct _GdkWaylandGLContextClass
 
 gboolean        gdk_wayland_display_init_gl                         (GdkDisplay        *display);
 GdkGLContext *  gdk_wayland_window_create_gl_context                (GdkWindow         *window,
+                                                                    gboolean           attach,
                                                                      GdkGLProfile       profile,
                                                                      GdkGLContext      *share,
                                                                      GError           **error);
index b72b3594fb336697c505bc3d617f650340e8d9a9..4f6f15b3786bd37131bea210bc97cf26a3fb280c 100644 (file)
@@ -239,5 +239,7 @@ void gdk_wayland_selection_unset_data_source (GdkAtom selection);
 
 EGLSurface gdk_wayland_window_get_egl_surface (GdkWindow *window,
                                                EGLConfig config);
+EGLSurface gdk_wayland_window_get_dummy_egl_surface (GdkWindow *window,
+                                                    EGLConfig config);
 
 #endif /* __GDK_PRIVATE_WAYLAND_H__ */
index 90d75efbabb16f625d08950391da2f6a526718d7..828cd57358ee39b4ae3b4e1634ef0f48f97aef65 100644 (file)
@@ -101,6 +101,9 @@ struct _GdkWindowImplWayland
   struct wl_egl_window *egl_window;
   EGLSurface egl_surface;
 
+  struct wl_egl_window *dummy_egl_window;
+  EGLSurface dummy_egl_surface;
+
   unsigned int mapped : 1;
   unsigned int use_custom_surface : 1;
   unsigned int pending_commit : 1;
@@ -1171,6 +1174,18 @@ gdk_wayland_window_hide_surface (GdkWindow *window)
 
   if (impl->surface)
     {
+      if (impl->dummy_egl_surface)
+        {
+          eglDestroySurface(display_wayland->egl_display, impl->dummy_egl_surface);
+          impl->dummy_egl_surface = NULL;
+        }
+
+      if (impl->dummy_egl_window)
+        {
+          wl_egl_window_destroy (impl->dummy_egl_window);
+          impl->dummy_egl_window = NULL;
+        }
+
       if (impl->egl_surface)
         {
           eglDestroySurface(display_wayland->egl_display, impl->egl_surface);
@@ -2238,6 +2253,32 @@ gdk_wayland_window_get_egl_surface (GdkWindow *window,
   return impl->egl_surface;
 }
 
+EGLSurface
+gdk_wayland_window_get_dummy_egl_surface (GdkWindow *window,
+                                         EGLConfig config)
+{
+  GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
+  GdkWindowImplWayland *impl;
+  struct wl_egl_window *egl_window;
+
+  g_return_val_if_fail (GDK_IS_WAYLAND_WINDOW (window), NULL);
+
+  impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+
+  if (impl->dummy_egl_surface == NULL)
+    {
+      impl->dummy_egl_window =
+        wl_egl_window_create (impl->surface, 1, 1);
+
+      impl->dummy_egl_surface =
+        eglCreateWindowSurface (display_wayland->egl_display,
+                                config, impl->dummy_egl_window, NULL);
+    }
+
+  return impl->dummy_egl_surface;
+}
+
+
 /**
  * gdk_wayland_window_set_use_custom_surface:
  * @window: (type GdkWaylandWindow): a #GdkWindow
index 70ff6a3d21a89b0e2cbcacbf373f2d2226097935..b4e4e57b40604f5ef2b7134d2e67397e90d8ad76 100644 (file)
 G_DEFINE_TYPE (GdkX11GLContext, gdk_x11_gl_context, GDK_TYPE_GL_CONTEXT)
 
 typedef struct {
-  GLXDrawable drawable;
-
   GdkDisplay *display;
-  GdkWindow *window;
+
+  GLXDrawable glx_drawable;
+
+  Window dummy_xwin;
+  GLXWindow dummy_glx;
 
   guint32 last_frame_counter;
 } DrawableInfo;
@@ -53,11 +55,20 @@ static void
 drawable_info_free (gpointer data_)
 {
   DrawableInfo *data = data_;
+  Display *dpy;
 
   gdk_x11_display_error_trap_push (data->display);
 
-  if (data->drawable)
-    glXDestroyWindow (gdk_x11_display_get_xdisplay (data->display), data->drawable);
+  dpy = gdk_x11_display_get_xdisplay (data->display);
+
+  if (data->glx_drawable)
+    glXDestroyWindow (dpy, data->glx_drawable);
+
+  if (data->dummy_glx)
+    glXDestroyWindow (dpy, data->dummy_glx);
+
+  if (data->dummy_xwin)
+    XDestroyWindow (dpy, data->dummy_xwin);
 
   gdk_x11_display_error_trap_pop_ignored (data->display);
 
@@ -144,9 +155,10 @@ gdk_x11_window_invalidate_for_new_frame (GdkWindow      *window,
 
   buffer_age = 0;
 
-  if (display_x11->has_glx_buffer_age)
+  if (display_x11->has_glx_buffer_age &&
+      gdk_gl_context_make_current (window->gl_paint_context))
     glXQueryDrawable(dpy, context_x11->drawable,
-                     GLX_BACK_BUFFER_AGE_EXT, &buffer_age);
+                    GLX_BACK_BUFFER_AGE_EXT, &buffer_age);
 
   invalidate_all = FALSE;
   if (buffer_age == 0 || buffer_age >= 4)
@@ -500,14 +512,13 @@ gdk_x11_display_init_gl (GdkDisplay *display)
 #define MAX_GLX_ATTRS   30
 
 static gboolean
-find_fbconfig_for_window (GdkWindow         *window,
+find_fbconfig_for_visual (GdkDisplay        *display,
+                         GdkVisual         *visual,
                           GLXFBConfig       *fb_config_out,
                           XVisualInfo      **visinfo_out,
                           GError           **error)
 {
   static int attrs[MAX_GLX_ATTRS];
-  GdkVisual *visual = gdk_window_get_visual (window);
-  GdkDisplay *display = gdk_window_get_display (window);
   Display *dpy = gdk_x11_display_get_xdisplay (display);
   GLXFBConfig *configs;
   int n_configs, i;
@@ -650,12 +661,14 @@ create_gl_context (GdkDisplay   *display,
 
 GdkGLContext *
 gdk_x11_window_create_gl_context (GdkWindow    *window,
-                                  GdkGLProfile  profile,
-                                  GdkGLContext *share,
-                                  GError      **error)
+                                 gboolean      attached,
+                                 GdkGLProfile  profile,
+                                 GdkGLContext *share,
+                                 GError      **error)
 {
-  GdkDisplay *display = gdk_window_get_display (window);
+  GdkDisplay *display;
   GdkX11GLContext *context;
+  GdkVisual *visual;
   GdkVisual *gdk_visual;
   GLXFBConfig config;
   GLXContext glx_context;
@@ -665,6 +678,8 @@ gdk_x11_window_create_gl_context (GdkWindow    *window,
   Display *dpy;
   DrawableInfo *info;
 
+  display = gdk_window_get_display (window);
+
   if (!gdk_x11_display_init_gl (display))
     {
       g_set_error_literal (error, GDK_GL_ERROR,
@@ -684,7 +699,9 @@ gdk_x11_window_create_gl_context (GdkWindow    *window,
       return NULL;
     }
 
-  if (!find_fbconfig_for_window (window, &config, &xvisinfo, error))
+  visual = gdk_window_get_visual (window);
+
+  if (!find_fbconfig_for_visual (display, visual, &config, &xvisinfo, error))
     return NULL;
 
   dpy = gdk_x11_display_get_xdisplay (display);
@@ -712,31 +729,53 @@ gdk_x11_window_create_gl_context (GdkWindow    *window,
 
   is_direct = glXIsDirect (dpy, glx_context);
 
-  gdk_x11_display_error_trap_push (display);
-
-  if (GDK_X11_DISPLAY (display)->glx_version >= 13)
+  info = get_glx_drawable_info (window->impl_window);
+  if (info == NULL)
     {
-      info = get_glx_drawable_info (window->impl_window);
+      XSetWindowAttributes attrs;
+      unsigned long mask;
 
-      if (info == NULL)
-        {
-          info = g_slice_new (DrawableInfo);
-          info->window = window->impl_window;
-          info->display = display;
-          info->drawable = glXCreateWindow (dpy,
-                                            config,
-                                            gdk_x11_window_get_xid (window->impl_window),
-                                            NULL);
-          info->last_frame_counter = 0;
-
-          set_glx_drawable_info (window->impl_window, info);
-        }
+      gdk_x11_display_error_trap_push (display);
+
+      info = g_slice_new0 (DrawableInfo);
+      info->display = display;
+      info->last_frame_counter = 0;
+
+      attrs.override_redirect = True;
+      attrs.colormap = XCreateColormap (dpy, DefaultRootWindow (dpy), xvisinfo->visual, AllocNone);
+      attrs.border_pixel = 0;
+      mask = CWOverrideRedirect | CWColormap | CWBorderPixel;
+      info->dummy_xwin = XCreateWindow (dpy, DefaultRootWindow (dpy),
+                                       -100, -100, 1, 1,
+                                       0,
+                                       xvisinfo->depth,
+                                       CopyFromParent,
+                                       xvisinfo->visual,
+                                       mask,
+                                       &attrs);
+      XMapWindow(dpy, info->dummy_xwin);
+
+      if (GDK_X11_DISPLAY (display)->glx_version >= 13)
+       {
+         info->glx_drawable = glXCreateWindow (dpy, config,
+                                               gdk_x11_window_get_xid (window->impl_window),
+                                               NULL);
+         info->dummy_glx = glXCreateWindow (dpy, config, info->dummy_xwin, NULL);
+       }
 
-      drawable = info->drawable;
-    }
-  else
-    {
-      drawable = gdk_x11_window_get_xid (window);
+      if (gdk_x11_display_error_trap_pop (display))
+       {
+         g_set_error_literal (error, GDK_GL_ERROR,
+                              GDK_GL_ERROR_NOT_AVAILABLE,
+                              _("Unable to create a GL context"));
+
+         drawable_info_free (info);
+         glXDestroyContext (dpy, glx_context);
+
+         return NULL;
+       }
+
+      set_glx_drawable_info (window->impl_window, info);
     }
 
   gdk_visual = gdk_x11_screen_lookup_visual (gdk_display_get_default_screen (display),
@@ -744,16 +783,10 @@ gdk_x11_window_create_gl_context (GdkWindow    *window,
 
   XFree (xvisinfo);
 
-  if (gdk_x11_display_error_trap_pop (display))
-    {
-      g_set_error_literal (error, GDK_GL_ERROR,
-                           GDK_GL_ERROR_NOT_AVAILABLE,
-                           _("Unable to create a GL context"));
-
-      glXDestroyContext (dpy, glx_context);
-
-      return NULL;
-    }
+  if (attached)
+    drawable = info->glx_drawable ? info->glx_drawable : gdk_x11_window_get_xid (window->impl_window);
+  else
+    drawable = info->dummy_glx ? info->dummy_glx : info->dummy_xwin;
 
   GDK_NOTE (OPENGL,
             g_print ("Created GLX context[%p], %s\n",
@@ -765,9 +798,11 @@ gdk_x11_window_create_gl_context (GdkWindow    *window,
                           "visual", gdk_visual,
                           NULL);
 
+  context->profile = profile;
   context->glx_config = config;
   context->glx_context = glx_context;
   context->drawable = drawable;
+  context->is_attached = attached;
   context->is_direct = is_direct;
 
   return GDK_GL_CONTEXT (context);
@@ -832,7 +867,7 @@ gdk_x11_display_make_gl_context_current (GdkDisplay   *display,
   glXMakeContextCurrent (dpy, context_x11->drawable, context_x11->drawable,
                          context_x11->glx_context);
 
-  if (GDK_X11_DISPLAY (display)->has_glx_swap_interval)
+  if (context_x11->is_attached && GDK_X11_DISPLAY (display)->has_glx_swap_interval)
     {
       if (context_x11->do_frame_sync)
         glXSwapIntervalSGI (1);
index 9f7566ca5b04078ef8fc8fa97c6e9d618f5f3be8..091783e143c0c2136dac4b55dd193effb9f7bce8 100644 (file)
@@ -40,10 +40,12 @@ struct _GdkX11GLContext
 {
   GdkGLContext parent_instance;
 
+  GdkGLProfile profile;
   GLXContext glx_context;
   GLXFBConfig glx_config;
   GLXDrawable drawable;
 
+  guint is_attached : 1;
   guint is_direct : 1;
   guint do_frame_sync : 1;
 
@@ -56,6 +58,7 @@ struct _GdkX11GLContextClass
 
 gboolean        gdk_x11_display_init_gl                         (GdkDisplay        *display);
 GdkGLContext *  gdk_x11_window_create_gl_context                (GdkWindow         *window,
+                                                                gboolean           attached,
                                                                  GdkGLProfile       profile,
                                                                  GdkGLContext      *share,
                                                                  GError           **error);